#include "lex.hpp"
#include "ast.hpp"
#include "debug.hpp"
#include "analysis.hpp"
#include "codegen.hpp"

#include <cstdio>
#include <cstdlib>
#include <unistd.h>

#include <fstream>
#include <iostream>
using namespace std;


extern int yyparse();
extern ast::Program* parse_tree;

char* filename;
const char* output_filename = "a.out";
bool debug_mode = false;

  char cpp_cmd[1000];
  const char cpp_fmt[] = "cpp -D _RISCO_C_ -I . -traditional-cpp "
                         "-fextended-identifiers -fdollars-in-identifiers "
                         "-x assembler-with-cpp -P -C %s %s";

void usage();
void parse_args(int argc, char** argv);
void parse_input(int argc, char** argv);
int compile();
void finalize();


int main(int argc, char** argv) {
  cout << "Parsing args..." << endl;
  parse_args(argc, argv);

  cout << "Parsing input files..." << endl;
  parse_input(argc, argv);

  cout << "Compiling..." << endl;
  int ret = compile();

  cout << "Finalizing..." << endl;
  finalize();

  return ret;
}


void usage()
{
  cerr << "usage: risco-as -d [-o output] file1 [file2 ...]" << endl;
  exit(1);
}


void parse_args(int argc, char** argv)
{
  int c;
  opterr = 0;

  while ((c = getopt(argc, argv, "do:")) != -1) {
    switch (c) {
      case 'd':
        debug_mode = true;
        break;

      case 'o':
        output_filename = optarg;
        break;

      default:
        usage();
        break;
    }
  }
}


void parse_input(int argc, char** argv)
{
  if (optind == argc) {
    usage();
  }

  for (int i = optind; i < argc; ++i) {
    filename = argv[i];

    char tmp_name[] = "/tmp/risco_tmp_XXXXXX";
    mktemp(tmp_name);

    sprintf(cpp_cmd, cpp_fmt, filename, tmp_name);

    if (system(cpp_cmd)) {
      exit(1);
    }

    FILE* file = fopen(tmp_name, "r");
    yyrestart(file);
    yyparse();

    fclose(file);
  }
}


int compile()
{
  analysis::SemanticCheckPass checkPass(cerr);
  parse_tree->accept(&checkPass);

  if (!checkPass.succeeded()) {
    return 2;
  }

  analysis::SymbolTableBuildPass tableBuildPass(cerr);
  parse_tree->accept(&tableBuildPass);

  if (!tableBuildPass.succeeded()) {
    return 3;
  }

  analysis::SymbolResolverPass resolvePass(cerr,
    tableBuildPass.symbol_table,
    tableBuildPass.dataOffset,
    tableBuildPass.textOffset,
    *tableBuildPass.startLabel
  );

  parse_tree->accept(&resolvePass);

  if (!resolvePass.succeeded()) {
    return 4;
  }


  ofstream output(output_filename, ios::binary);

  codegen::BinaryCodeGeneratorPass codeGeneratorPass(
      cerr, output, resolvePass.startOffset()
  );
  
  parse_tree->accept(&codeGeneratorPass);

  if (!codeGeneratorPass.succeeded()) {
    return 5;
  }

  return 0;
}


void finalize()
{
  system("rm /tmp/risco_tmp*");
  delete parse_tree;
}
